home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung 2 / Power-Programmierung CD 2 (Tewi)(1994).iso / gnu / gnulib / gnucdiff / io.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-12-16  |  14.9 KB  |  542 lines

  1. /* File I/O for GNU DIFF.
  2.    Copyright (C) 1988 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY.  No author or distributor
  8. accepts responsibility to anyone for the consequences of using it
  9. or for whether it serves any particular purpose or works at all,
  10. unless he says so in writing.  Refer to the GNU DIFF General Public
  11. License for full details.
  12.  
  13. Everyone is granted permission to copy, modify and redistribute
  14. GNU DIFF, but only under the conditions described in the
  15. GNU DIFF General Public License.   A copy of this license is
  16. supposed to have been given to you along with GNU DIFF so you
  17. can know your rights and responsibilities.  It should be in a
  18. file named COPYING.  Among other things, the copyright notice
  19. and this notice must be preserved on all copies.  */
  20.  
  21. #include "diff.h"
  22.  
  23. /* Rotate a value n bits to the left. */
  24. #if !defined(MSDOS)
  25. #define UINT_BIT (sizeof (unsigned) * CHAR_BIT)
  26. #define ROL(v, n) ((v) << (n) | (v) >> UINT_BIT - (n))
  27. #else
  28. #define ROL(v,n)  ((v) << (n) | (v) >> (sizeof(v) * CHAR_BIT) - (n))
  29. #endif
  30.  
  31. /* Given a hash value and a new character, return a new hash value. */
  32. #define HASH(h, c) ((c) + ROL (h, 7))
  33.  
  34. /* Current file under consideration. */
  35. struct file_data *current;
  36.  
  37. /* Check for binary files and compare them for exact identity.  */
  38.  
  39. /* Return 1 if BUF contains a character with the 0200 bit set.
  40.    SIZE is the number of characters in BUF.  */
  41.  
  42. static int
  43. binary_file_p (buf, size)
  44.      char *buf;
  45.      int size;
  46. {
  47.   while (--size >= 0)
  48.     if (*buf++ & 0200)
  49.       return 1;
  50.   return 0;
  51. }
  52.  
  53. int binary_file_threshold = 512;
  54.  
  55. /* Slurp the current file completely into core.
  56.    Return nonzero if it appears to be a binary file.  */
  57.  
  58. static int
  59. slurp ()
  60. {
  61.   /* If we have a nonexistent file at this stage, treat it as empty.  */
  62.   if (current->desc < 0)
  63.     {
  64.       current->bufsize = 0;
  65.       current->buffered_chars = 0;
  66.       current->buffer = 0;
  67.     }
  68. #if !defined(MSDOS)     
  69.   /* If it's a regular file, we can just get the size out of the stat
  70.      block and slurp it in all at once. */
  71.   else if ((current->stat.st_mode & S_IFMT) == S_IFREG)
  72.     {
  73.       current->bufsize = current->stat.st_size + 1;
  74.       current->buffer = (char *) xmalloc (current->bufsize);
  75.       current->buffered_chars
  76.     = read (current->desc, current->buffer, current->bufsize);
  77. #else    /* MSDOS */
  78.   else {
  79.       current->bufsize = current->stat.st_size + 1;
  80.     if((current->buffer = 
  81.         (char huge *)halloc(current->bufsize,sizeof(char))) == NULL)
  82.         fatal("virtual memory exhausted");
  83.     {
  84.         int count;
  85.         current->buffered_chars = 0;
  86.         while((count = read(current->desc,
  87.             current->buffer+current->buffered_chars,
  88.                 32000)) > 0)
  89.             current->buffered_chars += count;
  90.     }
  91. #endif    /* MSDOS */
  92.       if (current->buffered_chars < 0)
  93.     pfatal_with_name (current->name);
  94.     }
  95. #if !defined(MSDOS)
  96.   else
  97.     {
  98.       int cc;
  99.  
  100.       current->bufsize = 4096;
  101.       current->buffer = (char *) xmalloc (current->bufsize);
  102.       current->buffered_chars = 0;
  103.       
  104.       /* Not a regular file; read it in a little at a time, growing the
  105.      buffer as necessary. */
  106.       while ((cc = read (current->desc,
  107.             current->buffer + current->buffered_chars,
  108.             current->bufsize - 1 - current->buffered_chars)) > 0)
  109.     {
  110.       current->buffered_chars += cc;
  111.       if (current->buffered_chars == current->bufsize - 1)
  112.         {
  113.           current->bufsize *= 2;
  114.           current->buffer = (char *) xrealloc (current->buffer, current->bufsize);
  115.         }
  116.     }
  117.       if (cc < 0)
  118.     pfatal_with_name (current->name);
  119.     }
  120. #endif /* MSDOS */
  121.   
  122.   /* Check first part of file to see if it's a binary file.  */
  123.   if (! always_text_flag
  124.       && binary_file_p (current->buffer,
  125.             min (current->buffered_chars, binary_file_threshold)))
  126.     return 1;
  127.  
  128.   /* If not binary, make sure text ends in a newline,
  129.      but remember that we had to add one.  */
  130.   if (current->buffered_chars > 0
  131.       && current->buffer[current->buffered_chars - 1] != '\n')
  132.     {
  133.       current->missing_newline = 1;
  134.       current->buffer[current->buffered_chars++] = '\n';
  135.     }
  136.   else
  137.     current->missing_newline = 0;
  138.  
  139.   return 0;
  140. }
  141.  
  142. /* Split the file into lines, simultaneously computing the hash codes for
  143.    each line. */
  144.  
  145. void
  146. find_and_hash_each_line ()
  147. {
  148.   unsigned h;
  149.   int i;
  150.   unsigned char *p = (unsigned char *) current->prefix_end, *ip, c;
  151.  
  152.   /* Attempt to get a good initial guess as to the number of lines. */
  153. #if !defined(MSDOS)
  154.   current->linbufsize = current->buffered_chars / 50 + 5;
  155.   current->linbuf
  156.     = (struct line_def *) xmalloc (current->linbufsize * sizeof (struct line_def));
  157. #else
  158.     long int buffersize;
  159.     current->linbufsize = (int)
  160.         (buffersize = (long int)(current->buffered_chars / 50 + 5));
  161.     buffersize *= sizeof(struct line_def);
  162.     if(buffersize > 65535L)
  163.         fatal("Too many lines to compare");
  164.     current->linbuf = (struct line_def *) xmalloc((size_t)buffersize);
  165. #endif
  166.  
  167.   if (function_regexp)
  168.     {
  169.       /* If the -C or -F option is used, we need to find the lines
  170.      of the matching prefix.  At least we will need to find the last few,
  171.      but since we don't know how many, it's easiest to find them all.  */
  172.       current->buffered_lines = 0;
  173.       p = (unsigned char *) current->buffer;
  174.     }
  175.   else
  176.     {
  177.       /* Skip the identical prefixes, except be prepared to handle context.
  178.      In fact, handle 1 more preceding line than the context says,
  179.      in case shift_boundaries moves things backwards in this file.  */
  180.       current->buffered_lines = current->prefix_lines - context - 1;
  181.       if (current->buffered_lines < 0)
  182.     current->buffered_lines = 0;
  183.       for (i = 0; i < context + 1; ++i)
  184.     while ((char *) p > current->buffer && (--p)[-1] != '\n')
  185.       ;
  186.     }
  187.  
  188.   while ((char *) p < current->suffix_begin)
  189.     {
  190.       h = 0;
  191.       ip = p;
  192.  
  193.       if (current->prefix_end <= (char *) p)
  194.     {
  195.       /* Hash this line until we find a newline. */
  196.       if (ignore_case_flag)
  197.         {
  198.           if (ignore_all_space_flag)
  199.         while ((c = *p) != '\n')
  200.           {
  201.             if (! isspace (c))
  202.               if (isupper (c))
  203.             h = HASH (h, tolower (c));
  204.               else
  205.             h = HASH (h, c);
  206.             ++p;
  207.           }
  208.           else if (ignore_space_change_flag)
  209.  
  210.         while ((c = *p) != '\n')
  211.           {
  212.             if (c == ' ' || c == '\t')
  213.               {
  214.             while ((c = *p) == ' ' || c == '\t')
  215.               ++p;
  216.             if (c == '\n')
  217.               break;
  218.             h = HASH (h, ' ');
  219.               }
  220.             else if (isupper (c))
  221.               h = HASH (h, tolower (c));
  222.             else
  223.               h = HASH (h, c);
  224.             ++p;
  225.           }
  226.           else
  227.         while ((c = *p) != '\n')
  228.           {
  229.             if (isupper (c))
  230.               h = HASH (h, tolower (c));
  231.             else
  232.               h = HASH (h, c);
  233.             ++p;
  234.           }
  235.         }
  236.       else
  237.         {
  238.           if (ignore_all_space_flag)
  239.         while ((c = *p) != '\n')
  240.           {
  241.             if (! isspace (c))
  242.               h = HASH (h, c);
  243.             ++p;
  244.           }
  245.           else if (ignore_space_change_flag)
  246.         while ((c = *p) != '\n')
  247.           {
  248.             if (c == ' ' || c == '\t')
  249.               {
  250.             while ((c = *p) == ' ' || c == '\t')
  251.               ++p;
  252.             if (c == '\n')
  253.               break;
  254.             h = HASH (h, ' ');
  255.               }
  256.             else
  257.               h = HASH (h, c);
  258.             ++p;
  259.           }
  260.           else
  261.         while ((c = *p) != '\n')
  262.           {
  263.             h = HASH (h, c);
  264.             ++p;
  265.           }
  266.         }
  267.     }
  268.       else
  269.     /* This line is part of the matching prefix,
  270.        so we don't need to hash it.  */
  271.     while (*p != '\n')
  272.       ++p;
  273.       
  274.       /* Maybe increase the size of the line table. */
  275.       if (current->buffered_lines >= current->linbufsize)
  276.     {
  277.       while (current->buffered_lines >= current->linbufsize)
  278.         current->linbufsize *= 2;
  279.       current->linbuf
  280.         = (struct line_def *) xrealloc (current->linbuf,
  281.                         current->linbufsize
  282.                         * sizeof (struct line_def));
  283.     }
  284.       current->linbuf[current->buffered_lines].text = (char *) ip;
  285.       current->linbuf[current->buffered_lines].length = p - ip;
  286.       current->linbuf[current->buffered_lines].hash = h;
  287.       ++current->buffered_lines;
  288.       ++p;
  289.     }
  290.  
  291.   i = 0;
  292.   while (i < context && (char *) p < current->buffer + current->buffered_chars)
  293.     {
  294.       ip = p;
  295.       while (*p++ != '\n')
  296.     ;
  297.       /* Maybe increase the size of the line table. */
  298.       if (current->buffered_lines >= current->linbufsize)
  299.     {
  300.       while (current->buffered_lines >= current->linbufsize)
  301.         current->linbufsize *= 2;
  302.       current->linbuf
  303.         = (struct line_def *) xrealloc (current->linbuf,
  304.                         current->linbufsize
  305.                         * sizeof (struct line_def));
  306.     }
  307.       current->linbuf[current->buffered_lines].text = (char *) ip;
  308.       current->linbuf[current->buffered_lines].length = p - ip - 1;
  309.       current->linbuf[current->buffered_lines].hash = 0;
  310.       ++current->buffered_lines;
  311.       ++i;
  312.     }
  313. }
  314.  
  315. /* Given a vector of two file_data objects, find the identical
  316.    prefixes and suffixes of each object. */
  317.  
  318. static void
  319. find_identical_ends (filevec)
  320.      struct file_data filevec[];
  321. {
  322.   char *p0, *p1, *end0;
  323.   int lines;
  324.   /* Number of bytes left to scan.  */
  325. #if !defined(MSDOS)
  326.   int bytes = min (filevec[0].buffered_chars, filevec[1].buffered_chars);
  327. #else
  328.   long int bytes = min (filevec[0].buffered_chars, filevec[1].buffered_chars);
  329. #endif
  330.   /* Find identical prefix.  */
  331.  
  332.   p0 = filevec[0].buffer;
  333.   p1 = filevec[1].buffer;
  334.   lines = 0;
  335.  
  336.   while (bytes > 0 && *p0 == *p1)
  337.     {
  338.       if (*p0 == '\n')
  339.     ++lines;
  340.       ++p0, ++p1, --bytes;
  341.     }
  342.  
  343.   /* Skip back to last line-beginning in the prefix.  */
  344.   while (p0 != filevec[0].buffer && p0[-1] != '\n')
  345.     --p0, --p1, ++bytes;
  346.  
  347.   /* Record the prefix.  */
  348.   filevec[0].prefix_end = p0;
  349.   filevec[1].prefix_end = p1;
  350.   filevec[0].prefix_lines = filevec[1].prefix_lines = lines;
  351.   
  352.   /* Find identical suffix.  */
  353.  
  354.   /* P0 and P1 point at the last chars not yet compared.  */
  355.   p0 = filevec[0].buffer + filevec[0].buffered_chars - 1;
  356.   p1 = filevec[1].buffer + filevec[1].buffered_chars - 1;
  357.   lines = 0;
  358.   end0 = p0;
  359.  
  360.   while (bytes > 0 && *p0 == *p1)
  361.     {
  362.       if (*p0 == '\n')
  363.     ++lines;
  364.       --p0, --p1, --bytes;
  365.     }
  366.   /* Now P0 and P1 point at the first char of the matching suffix.  */
  367.   ++p0, ++p1;
  368.  
  369.   /* Advance to next place that is a line-beginning in both files.  */
  370.   while (p0 != end0
  371.      && !((p0 == filevec[0].buffer || p0[-1] == '\n')
  372.           &&
  373.           (p1 == filevec[1].buffer || p1[-1] == '\n')))
  374.     ++p0, ++p1;
  375.  
  376.   /* Subtract one, since the last newline isn't followed by a line.  */
  377.   --lines;
  378.   
  379.   /* Record the suffix.  */
  380.   filevec[0].suffix_begin = p0;
  381.   filevec[1].suffix_begin = p1;
  382.   filevec[0].suffix_lines = filevec[1].suffix_lines = lines;
  383. }
  384.  
  385. /* Lines are put into equivalence classes (of lines that match in line_cmp).
  386.    Each equivalence class is represented by one of these structures,
  387.    but only while the classes are being computed.
  388.    Afterward, each class is represented by a number.  */
  389. struct equivclass
  390. {
  391.   struct equivclass *next;    /* Next item in this bucket. */
  392.   struct line_def line;    /* A line that fits this class. */
  393. };
  394.  
  395. /* Hash-table: array of buckets, each being a chain of equivalence classes.  */
  396. static struct equivclass **buckets;
  397.   
  398. /* Size of the bucket array. */
  399. static int nbuckets;
  400.  
  401. /* Array in which the equivalence classes are allocated.
  402.    The bucket-chains go through the elements in this array.
  403.    The number of an equivalence class is its index in this array.  */
  404. static struct equivclass *equivs;
  405.  
  406. /* Index of first free element in the array `equivs'.  */
  407. static int equivs_index;
  408.  
  409. /* Size allocated to the array `equivs'.  */
  410. static int equivs_alloc;
  411.  
  412. /* Largest primes less than some power of two, for nbuckets.  Values range
  413.    from useful to preposterous.  If one of these numbers isn't prime
  414.    after all, don't blame it on me, blame it on primes (6) . . . */
  415. static int primes[] =
  416. {
  417.   509,
  418.   1021,
  419.   2039,
  420.   4093,
  421.   8191,
  422.   16381,
  423.   32749,
  424.   65521,
  425. #if !defined MSDOS
  426.   131071,
  427.   262139,
  428.   524287,
  429.   1048573,
  430.   2097143,
  431.   4194301,
  432.   8388593,
  433.   16777213,
  434.   33554393,
  435.   67108859,            /* Preposterously large . . . */
  436.   -1
  437. #endif
  438. };
  439.  
  440. /* Index of current nbuckets in primes. */
  441. static int primes_index;
  442.  
  443. /* Find the equiv class associated with line N of the current file.  */
  444.  
  445. static int
  446. find_equiv_class (n)
  447.      int n;
  448. {
  449.   int bucket = (int)(current->linbuf[n].hash % nbuckets);
  450.   struct equivclass *b = buckets[bucket], *p = NULL;
  451.  
  452.   /* Equivalence class 0 is permanently allocated to lines that were
  453.      not hashed because they were parts of identical prefixes or
  454.      suffixes. */
  455.   if (n < current->prefix_lines
  456.       || current->linbuf[n].text >= current->suffix_begin)
  457.     return 0;
  458.  
  459.   /* Check through the appropriate bucket to see if there isn't already
  460.      an equivalence class for this line. */
  461.   while (b)
  462.     {
  463.       if (b->line.hash == current->linbuf[n].hash
  464.       && (b->line.length == current->linbuf[n].length
  465.           /* Lines of different lengths can match with certain options.  */
  466.           || length_varies)
  467.       && !line_cmp (&b->line, ¤t->linbuf[n]))
  468.     return b - equivs;
  469.       p = b, b = b->next;
  470.     }
  471.  
  472.   /* Create a new equivalence class in this bucket. */
  473.  
  474.   p = &equivs[equivs_index++];
  475.   p->next = buckets[bucket];
  476.   buckets[bucket] = p;
  477.   p->line = current->linbuf[n];
  478.  
  479.   return equivs_index - 1;
  480. }
  481.  
  482. /* Given a vector of two file_data objects, read the file associated
  483.    with each one, and build the table of equivalence classes.
  484.    Return nonzero if either file appears to be a binary file.  */
  485.  
  486. int
  487. read_files (filevec)
  488.      struct file_data filevec[];
  489. {
  490.   int i, j;
  491.   struct equivclass *b, *f;
  492.   int binary = 0;
  493.   int this_binary;
  494.  
  495.   current = &filevec[0];
  496.   binary = this_binary = slurp ();
  497.  
  498.   current = &filevec[1];
  499.   this_binary = slurp ();
  500.   if (binary || this_binary)
  501.     return 1;
  502.  
  503.   find_identical_ends (filevec);
  504.  
  505.   for (i = 0; i < 2; ++i)
  506.     {
  507.       current = &filevec[i];
  508.       find_and_hash_each_line ();
  509.     }
  510.  
  511.   /* This is guaranteed to be enough space.  */
  512.   equivs_alloc = filevec[0].buffered_lines + filevec[1].buffered_lines + 1;
  513.   equivs = (struct equivclass *) xmalloc (equivs_alloc * sizeof (struct equivclass));
  514.   /* Equivalence class 0 is permanently safe for lines that were not
  515.      hashed.  Real equivalence classes start at 1. */
  516.   equivs_index = 1;
  517.   
  518.   primes_index = 0;
  519.   while (primes[primes_index] < equivs_alloc / 3)
  520.     primes_index++;
  521.  
  522.   buckets = (struct equivclass **) xmalloc (primes[primes_index] * sizeof (struct equivclass *));
  523.   bzero (buckets, primes[primes_index] * sizeof (struct equivclass *));
  524.   nbuckets = primes[primes_index];
  525.  
  526.   for (i = 0; i < 2; ++i)
  527.     {
  528.       current = &filevec[i];
  529.       current->equivs
  530.     = (int *) xmalloc (current->buffered_lines * sizeof (int));
  531.       for (j = 0; j < current->buffered_lines; ++j)
  532.     current->equivs[j] = find_equiv_class (j);
  533.     }
  534.  
  535.   filevec[0].equiv_max = filevec[1].equiv_max = equivs_index;
  536.  
  537.   free (equivs);
  538.   free (buckets);
  539.  
  540.   return 0;
  541. }
  542.